package com.study.crypto.certificate.server.entity;

import com.study.crypto.dto.ca.CaConstants;
import com.study.crypto.dto.ca.RequestPkcs10Dto;
import com.study.crypto.dto.ca.RequestPkcs10Dto.RequestPkcs10DtoData;
import com.study.crypto.dto.ca.RequestSealCertDistinctNameDto;
import com.study.crypto.dto.ca.RequestSealCertDto;
import com.study.crypto.general.spring.common.GeneralEntity;
import com.study.crypto.utils.CertUtils;
import com.study.crypto.utils.bean.KeyValue;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.springframework.beans.BeanUtils;
import org.springframework.util.Assert;

import javax.persistence.Column;
import javax.persistence.Table;
import java.util.ArrayList;
import java.util.List;

/**
 * 表名：bo_certificate_apply
 * @author Songjin
 * @since 2021-01-10 14:11
 */
@Data
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
@SuperBuilder
@Table(name = "bo_certificate_apply")
public class CertificateApply extends GeneralEntity {
    
    /** 业务类型 */
    @Column(name = "task_code")
    private String taskCode;

    /** 协议版本号1.0 */
    @Column(name = "version")
    private String version;

    /** 客户端随机数与服务端随机数字符串拼接，中间无分隔符 */
    @Column(name = "token_info")
    private String tokenInfo;

    /** 用于区分请求列表的单个请求 */
    @Column(name = "request_id")
    private String requestId;

    /** 证书类型: 0 个人证书，1 单位证书，2 服务器证书，大于2预留扩展 */
    @Column(name = "cert_type")
    private String certType;

    /** 证书主体的国家项，2字节CN */
    @Column(name = "country_name")
    private String countryName;

    /** 证书主体的组织项，不大于 150 个字节 */
    @Column(name = "organization_name")
    private String organizationName;

    /** 证书主体的组织项，不大于200个字节 */
    @Column(name = "common_name")
    private String commonName;

    /** 证书主体DN */
    @Column(name = "dn")
    private String dn;

    /** 证书有效期起始时间，格式: yyyy-MM-dd HH:mm:ss */
    @Column(name = "not_before")
    private String notBefore;

    /** 证书有效期结束时间，格式: yyyy-MM-dd HH:mm:ss */
    @Column(name = "not_after")
    private String notAfter;

    /** SM2椭圆曲线公钥密码算法OID，1.2.156.10197.1.301 */
    @Column(name = "algorithm")
    private String algorithm;

    /** base64编码的签名公钥信息 */
    @Column(name = "subject_public_key_info")
    private String subjectPublicKeyInfo;

    /** base64编码的pkcs10 */
    @Column(name = "pkcs10")
    private String pkcs10;

    /** 密钥外键 */
    @Column(name = "keystore_id")
    private String keystoreId;
    
    /** CA系统返回的任务编号 */
    @Column(name = "task_id")
    private String taskId;
    
    public static List<CertificateApply> getInstances(RequestSealCertDto request) {
        List<CertificateApply> list = new ArrayList<>();
        request.getData().forEach(data -> {
            CertificateApply apply = new CertificateApply();
            BeanUtils.copyProperties(data, apply);
            BeanUtils.copyProperties(request, apply);
            String cn = apply.getCommonName();
            String o = apply.getOrganizationName();
            String c = apply.getCountryName();
            X500Name x500Name = CertUtils.buildDistinctName(c, o, cn);
            apply.setDn(x500Name.toString());
            list.add(apply);
        });
        return list;
    }

    public static List<CertificateApply> getInstances(RequestSealCertDistinctNameDto request) {
        List<CertificateApply> list = new ArrayList<>();
        request.getData().forEach(data -> {
            CertificateApply apply = new CertificateApply();
            List<KeyValue<ASN1ObjectIdentifier, String>> listDn = CertUtils.splitDn(data.getDn());
            BeanUtils.copyProperties(data, apply);
            BeanUtils.copyProperties(request, apply);
            // 填充 CountryName、CommonName、OrganizationName 属性值
            fillProperties(apply, listDn);
            list.add(apply);
        });
        return list;
    }

    public static List<CertificateApply> getInstances(RequestPkcs10Dto request) throws Exception {
        List<CertificateApply> list = new ArrayList<>();
        for (int i = 0; i < request.getData().size(); i++) {
            RequestPkcs10DtoData pkcs10Data = request.getData().get(i);
            String pkcs10Text = pkcs10Data.getPkcs10();
            PKCS10CertificationRequest csr = new PKCS10CertificationRequest(Base64.decodeBase64(pkcs10Text));
            SubjectPublicKeyInfo publicKeyInfo = csr.getSubjectPublicKeyInfo();
            JcaContentVerifierProviderBuilder providerBuilder = new JcaContentVerifierProviderBuilder();
            ContentVerifierProvider verifierProvider = providerBuilder.build(publicKeyInfo);
            boolean signatureValid = csr.isSignatureValid(verifierProvider);
            Assert.isTrue(signatureValid, "pkcs10自验失败");

            byte[] publicKeyBytes = publicKeyInfo.getPublicKeyData().getBytes();
            X500Name subject = csr.getSubject();
            String dn = subject.toString();
            List<KeyValue<ASN1ObjectIdentifier, String>> listDn = CertUtils.splitDn(dn);

            CertificateApply apply = new CertificateApply();
            BeanUtils.copyProperties(pkcs10Data, apply);
            BeanUtils.copyProperties(request, apply);
            apply.setCertType(CaConstants.CERT_TYPE_SERVER);
            ASN1Encodable parameters = publicKeyInfo.getAlgorithm().getParameters();
            if (parameters instanceof ASN1ObjectIdentifier) {
                apply.setAlgorithm(((ASN1ObjectIdentifier) publicKeyInfo.getAlgorithm().getParameters()).getId());
                apply.setSubjectPublicKeyInfo(Base64.encodeBase64String(publicKeyBytes));
            } else {
                apply.setAlgorithm(publicKeyInfo.getAlgorithm().getAlgorithm().getId());
                apply.setSubjectPublicKeyInfo(Base64.encodeBase64String(publicKeyInfo.getEncoded(ASN1Encoding.DER)));
            }
            // 填充 CountryName、CommonName、OrganizationName 属性值
            fillProperties(apply, listDn);
            apply.setPkcs10(pkcs10Text);
            apply.setDn(dn);
            list.add(apply);
        }
        return list;
    }

    private static void fillProperties(CertificateApply apply, List<KeyValue<ASN1ObjectIdentifier, String>> listDn) {
        for (KeyValue<ASN1ObjectIdentifier, String> kv : listDn) {
            if (kv.getKey().equals(BCStyle.C)) {
                apply.setCountryName(kv.getValue());
            } else if (kv.getKey().equals(BCStyle.CN)) {
                apply.setCommonName(kv.getValue());
            } else if (kv.getKey().equals(BCStyle.O)) {
                apply.setOrganizationName(kv.getValue());
            }
        }
    }
}